#include "rfid.h"
#include <cstring>
#include <cerrno>
#include <termios.h>
#include <unistd.h>
#include <sys/select.h>

// 实际定义全局变量
volatile unsigned int cardid = 0;
struct timeval timeout;

// 打开串口
int open_serial()
{
    int fd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd < 0)
    {
        perror("打开串口失败");
        return -1;
    }
    return fd;
}

// 初始化串口（波特率：9600）
void init_tty(int fd)
{
    struct termios termios_new;

    // 清空结构体
    memset(&termios_new, 0, sizeof(termios_new));

    // 设置串口为原始模式（无回显、无行缓冲等）
    cfmakeraw(&termios_new);

    // 设置输入/输出波特率为 9600
    cfsetispeed(&termios_new, B9600);
    cfsetospeed(&termios_new, B9600);

    // 启用本地连接和接收
    termios_new.c_cflag |= CLOCAL | CREAD;

    // 设置数据位为 8 位
    termios_new.c_cflag &= ~CSIZE;
    termios_new.c_cflag |= CS8;

    // 无奇偶校验
    termios_new.c_cflag &= ~PARENB;

    // 1 位停止位
    termios_new.c_cflag &= ~CSTOPB;

    // 设置超时和最小读取字节数
    termios_new.c_cc[VTIME] = 10;  // 超时时间（单位：0.1秒）
    termios_new.c_cc[VMIN] = 1;    // 最小读取字节数

    // 清空输入缓冲区
    tcflush(fd, TCIFLUSH);

    // 应用配置
    tcsetattr(fd, TCSANOW, &termios_new);
}

// 计算 BCC 校验码
unsigned char CalBCC(unsigned char *buf, int n)
{
    unsigned char bcc = 0;
    for (int i = 0; i < n; i++)
    {
        bcc ^= buf[i];
    }
    return ~bcc;
}

// 发送 'A' 命令（请求卡片）
int PiccRequest(int fd)
{
    unsigned char WBuf[128], RBuf[128];
    int ret, len = 0;
    fd_set rdfd;

    memset(WBuf, 0, sizeof(WBuf));
    memset(RBuf, 0, sizeof(RBuf));

    WBuf[0] = 0x07;  // 帧长度 = 7 字节
    WBuf[1] = 0x02;  // 命令类型
    WBuf[2] = 0x41;  // 'A' 命令
    WBuf[3] = 0x01;  // 信息长度
    WBuf[4] = 0x52;  // 模式：ALL=0x52
    WBuf[5] = CalBCC(WBuf, WBuf[0] - 2);  // BCC 校验
    WBuf[6] = 0x03;  // 结束符

    while (true)
    {
        FD_ZERO(&rdfd);
        FD_SET(fd, &rdfd);
        tcflush(fd, TCIFLUSH);

        write(fd, WBuf, 7);

        ret = select(fd + 1, &rdfd, NULL, NULL, &timeout);
        switch (ret)
        {
            case -1:
                perror("select 错误");
                break;
            case 0:
                len++;
                if (len == 3)  // 3 次超时后退出
                {
                    len = 0;
                    return -1;
                }
                break;
            default:
                usleep(10000);
                ret = read(fd, RBuf, 8);
                if (ret < 0)
                {
                    printf("读取错误: ret=%d, errno=%d\n", ret, errno);
                    break;
                }
                if (RBuf[2] == 0x00)  // 状态码为 0 表示成功
                {
                    return 0;
                }
                break;
        }
        usleep(100000);
    }
    return -1;
}

// 发送 'B' 命令（获取卡片 ID）
int PiccAnticoll(int fd)
{
    unsigned char WBuf[128], RBuf[128];
    int ret, len = 0;
    fd_set rdfd;

    memset(WBuf, 0, sizeof(WBuf));
    memset(RBuf, 0, sizeof(RBuf));

    WBuf[0] = 0x08;  // 帧长度 = 8 字节
    WBuf[1] = 0x02;  // 命令类型
    WBuf[2] = 0x42;  // 'B' 命令
    WBuf[3] = 0x02;  // 信息长度
    WBuf[4] = 0x93;  // 防碰撞模式
    WBuf[5] = 0x00;  // 位计数
    WBuf[6] = CalBCC(WBuf, WBuf[0] - 2);  // BCC 校验
    WBuf[7] = 0x03;  // 结束符

    while (true)
    {
        tcflush(fd, TCIFLUSH);
        FD_ZERO(&rdfd);
        FD_SET(fd, &rdfd);

        write(fd, WBuf, 8);

        ret = select(fd + 1, &rdfd, NULL, NULL, &timeout);
        switch (ret)
        {
            case -1:
                perror("select 错误");
                break;
            case 0:
                len++;
                if (len == 10)  // 10 次超时后退出
                {
                    len = 0;
                    return -1;
                }
                perror("超时");
                break;
            default:
                usleep(10000);
                ret = read(fd, RBuf, 10);
                if (ret < 0)
                {
                    printf("读取错误: ret=%d\n", ret);
                    break;
                }
                if (RBuf[2] == 0x00)  // 状态码为 0 表示成功
                {
                    cardid = (RBuf[4] << 24) | (RBuf[5] << 16) | (RBuf[6] << 8) | RBuf[7];
                    return 0;
                }
                break;
        }
    }
    return -1;
}
